﻿#region System Namespaces
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
#endregion

#region Styx Namespaces
using Styx;
using Styx.CommonBot;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
#endregion

namespace GiwinMonk.Helpers
{
    internal static class Unit
    {
        public static HashSet<uint> IgnoreMobs = new HashSet<uint>
            {
                52288, // Venomous Effusion (NPC near the snake boss in ZG. Its the green lines on the ground. We want to ignore them.)
                52302, // Venomous Effusion Stalker (Same as above. A dummy unit)
                52320, // Pool of Acid
                52525, // Bloodvenom

                52387, // Cave in stalker - Kilnara
            };

        public static IEnumerable<WoWPlayer> GroupMembers
        {
            get
            {
                ulong[] guids = StyxWoW.Me.GroupInfo.RaidMemberGuids;
                return (
                    from p in ObjectManager.GetObjectsOfType<WoWPlayer>(true, true)
                    where p.IsFriendly && guids.Any(g => g == p.Guid)
                    select p).ToList();
            }
        }

        public static IEnumerable<WoWPartyMember> GroupMemberInfos
        {
            get
            {
                return StyxWoW.Me.GroupInfo.RaidMembers;
            }
        }

        public static IEnumerable<WoWPlayer> NearbyGroupMembers
        {
            get
            {
                return GroupMembers.Where(p => p.DistanceSqr <= 40 * 40).ToList();
            }
        }

        public static IEnumerable<WoWUnit> NearbyUnfriendlyUnits(int Yards)
        {
            return ObjectManager.GetObjectsOfType<WoWUnit>(false, false).Where(p => ValidUnit(p) && p.DistanceSqr <= Yards * Yards).ToList();
        }
        public static IEnumerable<WoWUnit> TargetsWithinMeleeDistanceAndMeFacingTarget()
        {
            return
                ObjectManager.GetObjectsOfType<WoWUnit>(false, false).Where(
                    p =>
                    p != null &&
                    p.IsHostile &&
                    p.IsWithinMeleeRange &&
                    StyxWoW.Me.CurrentTarget != null &&
                    p.IsPlayer &&
                    p.Location.Distance(StyxWoW.Me.CurrentTarget.Location) < 3.5 &&
                    StyxWoW.Me.IsSafelyFacing(StyxWoW.Me.CurrentTarget));
        }
        public static IEnumerable<WoWPlayer> OtherRangedTargetsTargettingMeAndCastingHostileSpell()
        {
            return ObjectManager.GetObjectsOfType<WoWPlayer>(false, false).Where(o => o != null &&
                                                                                    o.IsHostile &&
                                                                                    o.IsCasting &&
                                                                                    o.CurrentTarget.IsMe &&
                                                                                    o.IsPlayer &&
                                                                                    o.Class == WoWClass.Mage ||
                                                                                    o.Class == WoWClass.Warlock ||
                                                                                    o.Class == WoWClass.Shaman ||
                                                                                    o.Class == WoWClass.Priest ||
                                                                                    o.Class == WoWClass.Druid);
        }
        public static IEnumerable<WoWPlayer> SomeoneIsCastingCrowdControlSpellOnMe()
        {
            return ObjectManager.GetObjectsOfType<WoWPlayer>(false, false).Where(p => p != null &&
                                                                                    p.IsHostile &&
                                                                                    p.IsPlayer &&
                                                                                    p.Distance < 40 &&
                                                                                    p.CurrentTarget.IsMe &&
                                                                                    !p.IsCastingHealingSpell);
        }
        public static IEnumerable<WoWUnit> DetectTotem()
        {
            return ObjectManager.GetObjectsOfType<WoWUnit>(false, false).Where(
                                o => o != null &&
                                     o.IsTotem &&
                                     o.Name == "Grounding Totem" &&
                                     o.Distance < 12 &&
                                     o.IsHostile).ToList();
        }
        public static IEnumerable<WoWPlayer> SwitchToTargetLowHealth()
        {
            return ObjectManager.GetObjectsOfType<WoWPlayer>(false, false).Where(o =>
                                                                                 o != null &&
                                                                                 o.IsPlayer &&
                                                                                 o.IsHostile &&
                                                                                 o.HealthPercent < 40 &&
                                                                                 StyxWoW.Me.CurrentTarget.HealthPercent >
                                                                                 60);
        }

        // Mage Iceblock
        // Paladin Bubble


        public static bool ValidUnit(WoWUnit p)
        {
            if (IgnoreMobs.Contains(p.Entry))
                return false;

            // Ignore shit we can't select/attack
            if (!p.CanSelect || !p.Attackable)
                return false;

            // Ignore friendlies!
            if (p.IsFriendly)
                return false;

            // Duh
            if (p.IsDead)
                return false;

            // If its a pet, lets ignore it please.
            //if (p.IsPet || p.OwnedByRoot != null)
            //    return false;

            // And ignore critters/non-combat pets
            if (p.IsNonCombatPet || p.IsCritter)
                return false;

            return true;
        }

        public static IEnumerable<WoWUnit> UnfriendlyUnitsNearTarget(float distance)
        {
            var dist = distance * distance;
            var curTarLocation = StyxWoW.Me.CurrentTarget.Location;
            return ObjectManager.GetObjectsOfType<WoWUnit>(false, false).Where(
                        p => ValidUnit(p) && p.Location.DistanceSqr(curTarLocation) <= dist).ToList();
        }

        /// <summary>
        ///  Check the aura count thats created by yourself by the name on specified unit
        /// </summary>
        /// <param name="aura"> The name of the aura in English. </param>
        /// <param name="unit"> The unit to check auras for. </param>
        /// <returns></returns>
        public static bool HasMyAura(this WoWUnit unit, string aura)
        {
            return HasMyAura(unit, aura, 0);
        }

        public static bool HasMyAura(this WoWUnit unit, string aura, int stacks)
        {
            return HasAura(unit, aura, stacks, StyxWoW.Me);
        }

        private static bool HasAura(this WoWUnit unit, string aura, int stacks, WoWUnit creator)
        {
            return unit.GetAllAuras().Any(a => a.Name == aura &&
                a.StackCount >= stacks &&
                (creator == null || a.CreatorGuid == creator.Guid));
        }

        public static bool HasAnyAura(this WoWUnit unit, params string[] auraNames)
        {
            var auras = unit.GetAllAuras();
            var hashes = new HashSet<string>(auraNames);
            return auras.Any(a => hashes.Contains(a.Name));
        }

        public static bool HasAuraWithMechanic(this WoWUnit unit, params WoWSpellMechanic[] mechanics)
        {
            var auras = unit.GetAllAuras();
            return auras.Any(a => mechanics.Contains(a.Spell.Mechanic));
        }

        public static List<WoWPlayer> ResurrectablePlayers
        {
            get
            {
                return ObjectManager.GetObjectsOfType<WoWPlayer>().Where(
                    p => !p.IsMe && p.IsDead && p.IsFriendly && p.IsInMyPartyOrRaid &&
                         p.DistanceSqr < 40 * 40 && !Blacklist.Contains(p.Guid)).ToList();
            }
        }

        public static bool IsCrowdControlled(this WoWUnit unit)
        {
            Dictionary<string, WoWAura>.ValueCollection auras = unit.Auras.Values;

            return auras.Any(
                a => a.Spell.Mechanic == WoWSpellMechanic.Asleep ||
                    a.Spell.Mechanic == WoWSpellMechanic.Horrified ||
                    a.Spell.Mechanic == WoWSpellMechanic.Fleeing ||
                    a.Spell.Mechanic == WoWSpellMechanic.Charmed ||
                    a.Spell.Mechanic == WoWSpellMechanic.Frozen ||
                    a.Spell.Mechanic == WoWSpellMechanic.Polymorphed ||
                    a.Spell.Mechanic == WoWSpellMechanic.Rooted ||
                    a.Spell.Mechanic == WoWSpellMechanic.Stunned ||
                    a.Spell.Mechanic == WoWSpellMechanic.Disoriented ||
                    a.Spell.Mechanic == WoWSpellMechanic.Incapacitated
                     );
        }

        public static bool IsInGroup(this LocalPlayer me)
        {
            return StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid;
        }
    }
}
